﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FileDownLoad
{
    public delegate void InvokeDelegate();
    public partial class Form1 : Form
    {
        #region 变量
        /// <summary>
        /// 已完成字节数
        /// </summary>
        private long completedCount = 0;
        /// <summary>
        /// 是否完成
        /// </summary>
       volatile private bool isCompleted = true;
        /// <summary>
        /// 数据块队列
        /// </summary>
        private ConcurrentQueue<MemoryStream> msQueue = new ConcurrentQueue<MemoryStream>();
        /// <summary>
        /// 下载开始位置
        /// </summary>
        private long range = 0;
        /// <summary>
        /// 文件大小
        /// </summary>
        private long total = 0;
        /// <summary>
        /// 一段时间内的完成节点数，计算网速用
        /// </summary>
        private long unitCount = 0;
        /// <summary>
        /// 上次计时时间，计算网速用
        /// </summary>
        private DateTime lastTime = DateTime.MinValue;
        /// <summary>
        /// 一段时间内的完成字节数，控制网速用
        /// </summary>
        private long unitCountForLimit = 0;
        /// <summary>
        /// 上次计时时间，控制网速用
        /// </summary>
        private DateTime lastTimeForLimit = DateTime.MinValue;
        /// <summary>
        /// 下载文件sleep时间，控制速度用
        /// </summary>
        private int sleepTime = 1;
        private bool cancle = false;
        private bool saveCancle = false; //取消保存
        private AutoResetEvent downloadEvent = new AutoResetEvent(false);
        private AutoResetEvent saveEvent = new AutoResetEvent(false);
        private string url;
        private string filePath;
        private bool BusyFlag = false; //任务忙标志

        //private uint result

        #endregion
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            lblMsg.Text = string.Empty;
            lblByteMsg.Text = string.Empty;
            lblSpeed.Text = string.Empty;
            DownLoadInit();

        }
        #region btnDownload_Click 下载
        private void btnDownload_Click(object sender, EventArgs e)
        {
            string url = txtUrl.Text.Trim();
            StartDownLoad(url);
        }
        #endregion

        void StartDownLoad(string _url)
        {
            if (BusyFlag == true)
            {
                throw new Exception("DownLoad Is Busy");
            }
            BusyFlag = true;
            isCompleted = false;
            cancle = false;
            saveCancle = false;
            url = _url;
            filePath = CreateFilePath(url);

            downloadEvent.Set();
            saveEvent.Set();

        }

        void StopDownLoad()
        {
            cancle = true;
        }

        #region 现在初始化函数
        void DownLoadInit()
        {
            isCompleted = false;
            #region 下载线程
            Thread thread = new Thread(new ThreadStart(() =>
            {
                
                while (true)
                {
                    downloadEvent.WaitOne();
                    int tryTimes = 0;
                    while (!HttpDownloadFile(url, filePath))
                    {
                        this.Invoke(new InvokeDelegate(() =>
                        {
                            lblMsg.Text = "下载出错，尝试重新开始" + (++tryTimes).ToString();
                        }));
                        HttpDownloadFile(url, filePath);
                        Thread.Sleep(1);
                    }
                }

            }));
            thread.Name = "下载线程";
            thread.IsBackground = true;
            thread.Start();
            #endregion

            #region 保存文件线程
            thread = new Thread(new ThreadStart(() =>
            {
                while (true)
                {
                    saveEvent.WaitOne();
                    while (!isCompleted)
                    {
                        MemoryStream ms;
                        if (msQueue.TryDequeue(out ms))
                        {
                            using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Write))
                            {
                                fs.Seek(completedCount, SeekOrigin.Begin);
                                fs.Write(ms.ToArray(), 0, (int)ms.Length);
                                fs.Close();
                            }
                            completedCount += ms.Length;
                        }

                        if (total != 0 && total == completedCount)
                        {
                            Thread.Sleep(200);
                            isCompleted = true;
                            BusyFlag = false;
                        }

                        if (saveCancle == true)
                        {
                            while (msQueue.TryDequeue(out ms))
                            {
                                using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Write))
                                {
                                    fs.Seek(completedCount, SeekOrigin.Begin);
                                    fs.Write(ms.ToArray(), 0, (int)ms.Length);
                                    fs.Close();
                                }
                                Thread.Sleep(1);
                            }
                            Thread.Sleep(200);
                            isCompleted = true;
                            BusyFlag = false;

                        }

                        Thread.Sleep(1);
                    }
                }

            }));
            thread.Name = "存储线程";
            thread.IsBackground = true;
            thread.Start();
            #endregion


        }
        #endregion
        #region HttpDownloadFile 下载文件
        /// <summary>
        /// Http下载文件
        /// </summary>
        public bool HttpDownloadFile(string url, string filePath)
        {
            try
            {
                if (!File.Exists(filePath))
                {
                    using (FileStream fs = new FileStream(filePath, FileMode.Create))
                    {
                        fs.Close();
                    }
                }
                else
                {
                    FileInfo fileInfo = new FileInfo(filePath);
                    range = fileInfo.Length;
                }

                // 设置参数
                HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
                request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)";
                request.Proxy = null;
                request.Timeout = 5000;
                request.ReadWriteTimeout = 10000;
                //request.t
                //发送请求并获取相应回应数据
                HttpWebResponse response = request.GetResponse() as HttpWebResponse;
                if (response.ContentLength == range)
                {
                    this.Invoke(new InvokeDelegate(() =>
                    {
                        lblMsg.Text = "文件已下载";
                    }));
                    return true;
                }

                // 设置参数
                request = WebRequest.Create(url) as HttpWebRequest;
                request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)";
                request.Proxy = null;
                request.AddRange(range);
                request.Timeout = 5000;
                request.ReadWriteTimeout = 10000;
                //发送请求并获取相应回应数据
                response = request.GetResponse() as HttpWebResponse;
                //response = request.GetResponse() as HttpWebResponse;
                //直到request.GetResponse()程序才开始向目标网页发送Post请求
                Stream responseStream = response.GetResponseStream();

                total = range + response.ContentLength;
                completedCount = range;

                MemoryStream ms = new MemoryStream();
                byte[] bArr = new byte[2048];
                lastTime = DateTime.Now;
                lastTimeForLimit = DateTime.Now;
                cancle = false;
                saveCancle = false;
                int size = responseStream.Read(bArr, 0, (int)bArr.Length);
                unitCount += size;
                unitCountForLimit += size;
                ms.Write(bArr, 0, size);
                while (!isCompleted)
                {
                    if (cancle == true)
                    {
                        saveCancle = true;
                        return true;
                    }
                    size = responseStream.Read(bArr, 0, (int)bArr.Length);
                    unitCount += size;
                    unitCountForLimit += size;
                    ms.Write(bArr, 0, size);
                    if (ms.Length > 102400)
                    {
                        msQueue.Enqueue(ms);
                        ms = new MemoryStream();
                    }
                    if (completedCount + ms.Length == total)
                    {
                        msQueue.Enqueue(ms);
                        ms = new MemoryStream();
                    }
                    SpeedPid();
                    DownloadProcess();
                    Thread.Sleep(sleepTime);
                }
                responseStream.Close();
                return true;
            }
            catch
            {
                return false;
            }
        }
        #endregion
        #region 网速调整函数
        void SpeedPid()
        {
            if (!isCompleted)
            {
                TimeSpan ts = DateTime.Now.Subtract(lastTimeForLimit);
                if (ts.TotalMilliseconds > 200)
                {
                    double sec = ts.TotalSeconds;
                    double speed = unitCountForLimit / sec / 1024;

                    try
                    {
                        #region 限速/解除限速
                        double limitSpeed = 0;
                        if (double.TryParse(txtSpeed.Text.Trim(), out limitSpeed))
                        {
                            if (speed > limitSpeed && sleepTime < 1000)
                            {
                                sleepTime += 1;
                            }
                            if (speed < limitSpeed - 10 && sleepTime >= 2)
                            {
                                sleepTime -= 1;
                            }
                        }
                        else
                        {
                            this.Invoke(new InvokeDelegate(() =>
                            {
                                txtSpeed.Text = "100";
                            }));
                        }
                        #endregion
                    }
                    catch { }
                    Console.WriteLine("sleepTime:" + sleepTime.ToString());
                    lastTimeForLimit = DateTime.Now;
                    unitCountForLimit = 0;
                }

            }
        }
        #endregion
        #region 下载进度计算
        void DownloadProcess()
        {
            if (!isCompleted)
            {

                TimeSpan ts = DateTime.Now.Subtract(lastTime);
                if (ts.TotalMilliseconds > 1000)
                {
                    double sec = ts.TotalSeconds;
                    double speed = unitCount / sec / 1024;

                    try
                    {
                        #region 显示速度
                        if (speed < 1024)
                        {
                            this.Invoke(new InvokeDelegate(() =>
                            {
                                lblSpeed.Text = string.Format("{0}KB/S", speed.ToString("0.00"));
                            }));
                        }
                        else
                        {
                            this.Invoke(new InvokeDelegate(() =>
                            {
                                lblSpeed.Text = string.Format("{0}MB/S", (speed / 1024).ToString("0.00"));
                            }));
                        }
                        #endregion

                        #region 显示进度
                        this.Invoke(new InvokeDelegate(() =>
                        {
                            string strTotal = (total / 1024 / 1024).ToString("0.00") + "MB";
                            if (total < 1024 * 1024)
                            {
                                strTotal = (total / 1024).ToString("0.00") + "KB";
                            }
                            string completed = (completedCount / 1024 / 1024).ToString("0.00") + "MB";
                            if (completedCount < 1024 * 1024)
                            {
                                completed = (completedCount / 1024).ToString("0.00") + "KB";
                            }
                            lblMsg.Text = string.Format("进度：{0}/{1}", completed, strTotal);
                            lblByteMsg.Text = string.Format("已下载：{0}\r\n总大小：{1}", completedCount, total);

                            if (completedCount == total)
                            {
                                MessageBox.Show("完成");
                            }
                        }));
                        #endregion
                    }
                    catch { }

                    lastTime = DateTime.Now;
                    unitCount = 0;
                }
            }

        }

        #endregion

        #region 根据URL生成文件保存路径
        private string CreateFilePath(string url)
        {
            string path = Application.StartupPath + "\\download";
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }

            string fileName = Path.GetFileName(url);
            if (fileName.IndexOf("?") > 0)
            {
                return path + "\\" + fileName.Substring(0, fileName.IndexOf("?"));
            }
            else
            {
                return path + "\\" + fileName;
            }
        }
        #endregion

        private void button1_Click(object sender, EventArgs e)
        {
            StopDownLoad();
        }

        private async void button2_Click(object sender, EventArgs e)
        {
            int speed;
            if (int.TryParse(txtSpeed.Text, out speed))
            { }
            else
            {
                speed = 500;
            }
            AsyncHttpFileDownLoad downLoader = new AsyncHttpFileDownLoad(txtUrl.Text, 3, speed);
            downLoader.DownLoadStart();
            DownLoadResult result = await Task.Run<DownLoadResult>(
                ()=> {
                    while (downLoader.IsCompleted == false)
                    {
                        Thread.Sleep(200);
                    }
                      return downLoader.Result;
                }
                );
            if (result == DownLoadResult.DownLoad_Ok)
            {
                MessageBox.Show("下载完成");
            }
        }
    }

}

